package net.sansi.v3correctserver.task;

import cn.hutool.extra.spring.SpringUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.config.matrixloader.DoubleMatrixFileLoader;
import net.sansi.v3correctserver.constenum.AppHelper;
import net.sansi.v3correctserver.entity.image.RecCmmImage;
import net.sansi.v3correctserver.entity.image.RecImage;
import net.sansi.v3correctserver.entity.screen.Rect;
import net.sansi.v3correctserver.entity.space.CorrectCnf;
import net.sansi.v3correctserver.service.camera.Camera;
import net.sansi.v3correctserver.service.correct.bright.RectBrightCorrect;
import net.sansi.v3correctserver.service.space.ProjectSpace;
import net.sansi.v3correctserver.service.takeshowphoto.TakeShowPhotoService;
import net.sansi.v3correctserver.util.CmmUtil;
import net.sansi.v3correctserver.util.CorrectUtil;
import net.sansi.v3correctserver.util.Matrix;
import net.sansi.v3correctserver.util.RGBUtil;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by Fang
 * create time : 2024/11/1
 * IDE : IntelliJ IDEA
 * <p>
 * 亮度矫正任务
 */

@Slf4j
@Getter
@Setter
public class BrightCorrectTaskMock extends V3Task<BrightCorrectTaskMock, BrightCorrectTask.CorrectProgress> {

    private final BrightCorrectTask.Params params;
    private final Matrix<double[]> result;
    private final TakeShowPhotoService takeShowPhotoService;
    private final ProjectSpace projectSpace;
    private final CorrectCnf correctCnf;
    private final Rect showArea;
    private final Matrix<Set<Long>> ledBorders;

    public BrightCorrectTaskMock(BrightCorrectTask.Params params) {
        super(5, new BrightCorrectTask.CorrectProgress());
        this.params = params;
        takeShowPhotoService = SpringUtil.getBean(TakeShowPhotoService.class);
        projectSpace = AppHelper.projectSpace();
        correctCnf = projectSpace.getCorrectCnf();
        showArea = params.getArea().getShowArea();
        int w = showArea.getWidth(), h = showArea.getHeight();
        result = new Matrix<>(w, h, (x, y) -> new double[]{0, 0, 0});
        ledBorders = new Matrix<>(w, h, (x, y) -> new HashSet<>());

        // 设置进度条
        int span = correctCnf.getShowSpan();
        progress.getTotal()[0] = span * span;
        progress.getTotal()[1] = span * span;
        progress.getTotal()[2] = span * span;

    }

    @Override
    void runHandler() throws Exception {
        log.info("[区域亮度矫正开始] [area] {}", params.getArea());
        for (int i = 0; i < 3; i++) {
            toCorrectColor(i);
        }
    }

    // 黑色背景相片
    RecImage blackPhoto = null;

    /**
     * 对单个颜色进行矫正
     *
     * @param colorIdx 颜色索引
     * @throws InterruptedException
     */
    private void toCorrectColor(int colorIdx) throws InterruptedException {
        log.info("开始矫正颜色 [color] {}", RGBUtil.colorName(colorIdx));
        if (correctCnf.getReuseResult() && loadResult(colorIdx)) {
            log.info("加载已存在矫正结果");
            return;
        }
        log.info("正在拍摄 {} 背景", RGBUtil.colorName(colorIdx));
        CmmUtil.sleep(3000);
        int span = correctCnf.getShowSpan();
        RectBrightCorrect.Params corrParams = buildCorrectParams();
        corrParams.setBrightDecay(correctCnf.getBrightDecayFactor()[colorIdx]);
        RectBrightCorrect correct = new RectBrightCorrect(corrParams);
        List<Thread> tasks = new ArrayList<>();
        for (int ofx = 0; ofx < span; ofx++) {
            for (int ofy = 0; ofy < span; ofy++) {
                log.info("开始拍摄相片 [color] {} [offsetX] {} [offsetY] {}", RGBUtil.colorName(colorIdx), ofx, ofy);
                log.info("开始拍摄高亮相片");
                CmmUtil.sleep(2000);
                boolean isFirst = ofx == 0 && ofy == 0;
                if (isFirst) {
                    log.info("开始拍摄低亮相片");
                    CmmUtil.sleep(2000);
                } else log.info("不是第一张，跳过低亮相片拍摄");
                int[] offset = new int[]{ofx, ofy};
                Thread task = new Thread(() -> {
                    try {
                        if (!isFirst) {
                            tasks.get(0).join();
                            CmmUtil.sleep(3000);
                        } else {
                            CmmUtil.sleep(6000);
                            progress.getTargetPhotoBright()[colorIdx] = 10000;
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    log.info("单帧矫正结束 [color] {} [offsetX] {} [offsetY] {}",
                            RGBUtil.colorName(colorIdx), offset[0], offset[1]
                    );
                    progress.getCorrected()[colorIdx]++;
                    notifyUpdateProgress();
                });
                task.start();
                tasks.add(task);
                checkInterrupted();
                progress.getPhotoTaken()[colorIdx]++;
                notifyUpdateProgress();
            }
        }
        for (Thread task : tasks) {
            task.join();
        }
        saveResult(colorIdx);
    }

    /**
     * 保存矫正结果
     *
     * @param colorIdx 颜色索引
     */
    private void saveResult(int colorIdx) {
        int areaId = params.getArea().getAreaId();

        log.info("[保存矫正结果] [areaId] {} [color] {} ", areaId, RGBUtil.colorName(colorIdx));

        result.foreach((x, y, c) -> {
            c[0] = 1.0;
            c[1] = 1.0;
            c[2] = 1.0;
        });

        Matrix<double[]> colorResult = CorrectUtil.pickColorFromResult(result, colorIdx);

        // 保存预览图
        RecCmmImage preview = CorrectUtil.result2Preview(colorResult, 128);

        preview.save(projectSpace.brightResultPreview(areaId, colorIdx));

        // 保存打屏预览图
        RecCmmImage show = CorrectUtil.preview2Show(
                preview,
                correctCnf.getScreenWidth(),
                correctCnf.getScreenHeight(),
                params.getArea().getShowArea().getLeft(),
                params.getArea().getShowArea().getTop()
        );
        show.save(projectSpace.brightResultShow(areaId, colorIdx));

        // 保存结果矩阵
        Matrix<Double> matrix = CorrectUtil.result2ColorMatrix(colorResult, colorIdx);
        matrix.setLoader(new DoubleMatrixFileLoader());
        matrix.save(projectSpace.brightResult(areaId, colorIdx));
    }

    /**
     * 加载结果矩阵
     *
     * @param colorIdx 颜色索引
     * @return 结果矩阵
     */
    private boolean loadResult(int colorIdx) {
        int areaId = params.getArea().getAreaId();
        Path path = projectSpace.brightResult(areaId, colorIdx);
        if (!path.toFile().exists()) return false;
        Matrix<Double> res = Matrix.load(new DoubleMatrixFileLoader(), path);
        res.foreach((x, y, c) -> {
            result.get(x, y)[colorIdx] = c;
        });
        return true;
    }


    /**
     * 构建矫正参数
     *
     * @return 矫正参数
     */
    private RectBrightCorrect.Params buildCorrectParams() {
        return new RectBrightCorrect.Params()
                .setSpan(correctCnf.getShowSpan())
                .setAttenuation(1.0)
                .setShowBrightness(computeShowBright())
                .setBlackPhoto(blackPhoto)
                .setFence(params.getFence())
                .setResult(result)
                .setLedBorders(ledBorders)
                .setAllowEmptyPercent(0.5);
    }

    /**
     * 计算打屏亮度
     *
     * @return 打屏亮度
     */
    private int[][] computeShowBright() {
        int[][] res = new int[3][2];
        List<Camera.Params> cameraParams = correctCnf.getCameraParams();
        for (int i = 0; i < 3; i++) {
            res[i][0] = (int) (cameraParams.get(i).getBrightness());
            res[i][1] = (int) (cameraParams.get(i).getBrightness() * correctCnf.getKDecayFactor());
        }
        return res;
    }


}
